/*
 * @Description: 
 * @FilePath: /STM32/USER/bsp/spi.c
 * @Author: Wei Zhou
 * @Github: https://github.com/zromyk
 * @Date: 2021-04-28 15:25:19
 * @LastEditors: Wei Zhou
 * @LastEditTime: 2021-04-28 15:25:19
 * @Copyright: Copyright  2017 muyiro. All rights reserved.
 */

#include "spi.h"
#include "nvic.h"
#include "rcc.h"

uint8_t TxBuffer_SPI1[TxBuffer_Size_SPI1];
uint8_t RxBuffer_SPI1[RxBuffer_Size_SPI1];

uint8_t TxBuffer_SPI2[TxBuffer_Size_SPI2];
uint8_t RxBuffer_SPI2[RxBuffer_Size_SPI2];

SPI_Dev spi1 =
{
    .SPIx = SPI1,

    /*
                    0       1
        SPI1_NSS    PA4     PA15
        SPI1_SCK    PA5     PB3
        SPI1_MISO   PA6     PB4
        SPI1_MOSI   PA7     PB5
    */
    .RCC_Periph_SPI     = RCC_APB2Periph_SPI1,
    .GPIO_AF_SPI        = GPIO_AF_SPI1,
    .pin_nss            = PA4,
    .pin_sck            = PA5,
    .pin_miso           = PA6,
    .pin_mosi           = PA7,

    .tx_buf             = TxBuffer_SPI1,
    .rx_buf             = RxBuffer_SPI1,
    .tx_buf_size        = TxBuffer_Size_SPI1,
    .rx_buf_size        = RxBuffer_Size_SPI1,
    .rx_cnt             = 0,

    .SPIx_IRQn = SPI1_IRQn,

    /*              
        SPI1_TX     DMA2        DMA2
                    Stream  3   Stream  5
                    channel 3   channel 3
                    
        SPI1_RX     DMA2        DMA2
                    Stream  0   Stream  2
                    channel 3   channel 3
    */
    .dma.DMAx           = DMA2,
    .dma.RCC_Periph_DMA = RCC_AHB1Periph_DMA2,

    .dma.tx_stream      = DMA2_Stream3,
    .dma.tx_channel     = DMA_Channel_3,
    .dma.tx_FLAG_TCIF   = DMA_FLAG_TCIF3,

    .dma.rx_stream      = DMA2_Stream2,
    .dma.rx_channel     = DMA_Channel_3,
    .dma.rx_FLAG_TCIF   = DMA_FLAG_TCIF2};

SPI_Dev spi2 =
{
    .SPIx = SPI2,

    /*
                    0       1
        SPI2_NSS    PB12    PB9
        SPI2_SCK    PB13    PB10
        SPI2_MISO   PB14    PC2
        SPI2_MOSI   PB15    PC3
    */
    .RCC_Periph_SPI     = RCC_APB1Periph_SPI2,
    .GPIO_AF_SPI        = GPIO_AF_SPI2,
    .pin_nss            = PB12,
    .pin_sck            = PB13,
    .pin_miso           = PB14,
    .pin_mosi           = PB15,

    .tx_buf             = TxBuffer_SPI2,
    .rx_buf             = RxBuffer_SPI2,
    .tx_buf_size        = TxBuffer_Size_SPI2,
    .rx_buf_size        = RxBuffer_Size_SPI2,
    .rx_cnt             = 0,

    .SPIx_IRQn = SPI2_IRQn,

    /*              
        SPI2_TX     DMA1
                    Stream  4
                    channel 0
                    
        SPI2_RX     DMA1
                    Stream  3
                    channel 0
    */
    .dma.DMAx           = DMA1,
    .dma.RCC_Periph_DMA = RCC_AHB1Periph_DMA1,

    .dma.tx_stream      = DMA1_Stream4,
    .dma.tx_channel     = DMA_Channel_0,
    .dma.tx_FLAG_TCIF   = DMA_FLAG_TCIF4,

    .dma.rx_stream      = DMA1_Stream3,
    .dma.rx_channel     = DMA_Channel_0,
    .dma.rx_FLAG_TCIF   = DMA_FLAG_TCIF3
};

static void gpio_configuration(SPI_Dev *spi)
{
    gpio_af_init(spi->pin_sck, GPIO_PuPd_UP, spi->GPIO_AF_SPI);
    gpio_af_init(spi->pin_miso, GPIO_PuPd_NOPULL, spi->GPIO_AF_SPI);
    gpio_af_init(spi->pin_mosi, GPIO_PuPd_UP, spi->GPIO_AF_SPI);
}

static void spi_configuration(SPI_Dev *spi, SPI_Master_Slave master_slave, SPI_Mode mode)
{
    SPI_InitTypeDef SPI_InitStruct;

    rcc_periph_clock_enable(spi->SPIx, spi->RCC_Periph_SPI);

    /* SPI1 Config -------------------------------------------------------------*/
    /* Notes.  As far as I can tell, the AVR numbers for dataMode appear to match the numbers required by the STM32

    CPOL    CPHA    Shift SCK-edge  Capture SCK-edge
    0       0       Falling         Rising
    0       1       Rising          Falling
    1       0       Rising          Falling
    1       1       Falling         Rising

    On the STM32 it appears to be

    bit 1 - CPOL : Clock polarity
        (This bit should not be changed when communication is ongoing)
        0 : CLK to 0 when idle
        1 : CLK to 1 when idle

    bit 0 - CPHA : Clock phase
        (This bit should not be changed when communication is ongoing)
        0 : The first clock transition is the first data capture edge
        1 : The second clock transition is the first data capture edge
     
    If someone finds this is not the case or sees a logic error with this let me know ;-) 
    */

    SPI_InitStruct.SPI_Direction    = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStruct.SPI_Mode         = master_slave;            // SPI_Mode
    SPI_InitStruct.SPI_DataSize     = SPI_DataSize_8b; // 8 bit
    switch(mode)
    {
        case Mode0_CPOL0_CPHA0:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
            break;
        case Mode1_CPOL0_CPHA1:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
            break;
        case Mode2_CPOL1_CPHA0:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge;
            break;
        case Mode3_CPOL1_CPHA1:
            SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
            SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
            break;
    }
    SPI_InitStruct.SPI_NSS                  = SPI_NSS_Soft; // soft ss
    SPI_InitStruct.SPI_BaudRatePrescaler    = SPI_BaudRatePrescaler_16;
    SPI_InitStruct.SPI_FirstBit             = SPI_FirstBit_MSB;
    SPI_InitStruct.SPI_CRCPolynomial        = 7;

    SPI_Init(spi->SPIx, &SPI_InitStruct);
}

void spi_init(SPI_Dev *spi, SPI_Master_Slave master_slave, SPI_Mode mode)
{
    gpio_configuration(spi);
    spi_configuration(spi, master_slave, mode);
    SPI_CalculateCRC(spi->SPIx, ENABLE);

    /* Enable the SPIx */
    SPI_Cmd(spi->SPIx, ENABLE);
}

void spi_user_define_init(SPI_Dev *spi, SPI_InitTypeDef *SPI_InitStruct)
{
    gpio_configuration(spi);
    rcc_periph_clock_enable(spi->SPIx, spi->RCC_Periph_SPI);
    SPI_Init(spi->SPIx, SPI_InitStruct);
    SPI_CalculateCRC(spi->SPIx, ENABLE);

    /* Enable the SPIx */
    SPI_Cmd(spi->SPIx, ENABLE);
}

void spi_it_rx_init(SPI_Dev *spi, uint8_t priority, uint8_t subPriority)
{
    nvic_configuration(spi->SPIx_IRQn, priority, subPriority);
    /* Enable SPI_MASTER TXE interrupt */
    // SPI_I2S_ITConfig(spi->SPIx, SPI_I2S_IT_TXE, ENABLE);
    /* Enable SPI_SLAVE RXNE interrupt */
    SPI_I2S_ITConfig(spi->SPIx, SPI_I2S_IT_RXNE, ENABLE);
}

void spi_dma_rx_init(SPI_Dev *spi)
{
    dma_rx_init(&spi->dma, (uint32_t)&spi->SPIx->DR, spi->rx_buf, spi->rx_buf_size);

    SPI_I2S_DMACmd(spi->SPIx, SPI_I2S_DMAReq_Rx, ENABLE);
    DMA_Cmd(spi->dma.rx_stream, ENABLE);
}

void spi_dma_tx_init(SPI_Dev *spi)
{
    dma_tx_init(&spi->dma, (uint32_t)&spi->SPIx->DR, spi->tx_buf, spi->tx_buf_size);

    SPI_I2S_DMACmd(spi->SPIx, SPI_I2S_DMAReq_Tx, ENABLE);
    DMA_Cmd(spi->dma.rx_stream, ENABLE);
}

void spi_dma_tx_size_enable(SPI_Dev *spi, uint32_t size)
{
    assert_param(IS_SPI_Mode_Master(spi->SPI_Mode));

    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
    DMA_Cmd(spi->dma.tx_stream, DISABLE);
    DMA_SetCurrDataCounter(spi->dma.tx_stream, size);
    DMA_Cmd(spi->dma.tx_stream, ENABLE);
}

void spi_dma_tx_enable(SPI_Dev *spi)
{
    spi_dma_tx_size_enable(spi, spi->tx_buf_size);
}

void spi_write_byte(SPI_Dev *spi, const uint8_t data)
{
    SPI_I2S_SendData(spi->SPIx, data);
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
}

uint8_t spi_read_byte(SPI_Dev *spi)
{
    SPI_I2S_SendData(spi->SPIx, 0x00);
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
        ;
    while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_RXNE) == RESET)
        ;
    return SPI_I2S_ReceiveData(spi->SPIx);
}

void spi_write_buffer(SPI_Dev *spi, const uint8_t *buf, uint32_t length)
{
    while (length--) {
        SPI_I2S_SendData(spi->SPIx, *buf++);
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
            ;
    }
}

void spi_transfer(SPI_Dev *spi, uint8_t *tx, uint8_t *rx, uint32_t length)
{
    while (length--) {
        SPI_I2S_SendData(spi->SPIx, *tx++);
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_TXE) == RESET)
            ;
        while (SPI_I2S_GetFlagStatus(spi->SPIx, SPI_I2S_FLAG_RXNE) == RESET)
            ;
        *rx++ = SPI_I2S_ReceiveData(spi->SPIx);
    }
}
